﻿/* AgileFx Version 2.0
 * An open-source framework for rapid development of applications and services using Microsoft.net
 * Developed by: AgileHead
 * Website: www.agilefx.org
 * This component is licensed under the terms of the Apache 2.0 License.
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Web.Mvc;

using AgileFx.ORM;
using AgileFx.ORM.ModificationTypes;
using System.Threading;
using AgileFx.MVC.ViewModels;

namespace AgileFx.MVC.Controllers
{
    public class DefaultController<T> : Controller where T : DefaultViewModel, new()
    {
        class ViewModelUpdate
        {
            public Action<T> UpdateFunc { get; set; }

            bool isAsync = false;
            public bool IsAsync
            {
                get { return isAsync; }
                set { isAsync = value; }
            }
        }

        List<ViewModelUpdate> viewModelUpdates = new List<ViewModelUpdate>();
        Action<T> onCompleteActions;

        //This hides the Dictionary. Remove?
        public new object ViewData = null;
        List<Thread> activeThreads = new List<Thread>();

        protected void UpdateDefaultViewModel(Action<T> func)
        {
            viewModelUpdates.Add(new ViewModelUpdate { UpdateFunc = func, IsAsync = false });
        }

        protected void UpdateDefaultViewModelAsync(Action<T> func)
        {
            viewModelUpdates.Add(new ViewModelUpdate { UpdateFunc = func, IsAsync = true });
        }

        protected void UpdateDefaultViewModelOnComplete(Action<T> func)
        {
            onCompleteActions += func;
        }

        private X GetViewModel<X>() where X : T, new()
        {
            X viewModel = new X();

            foreach (var update in viewModelUpdates)
            {
                if (!update.IsAsync)
                    update.UpdateFunc(viewModel);
                else
                {
                    var updateFunc = update.UpdateFunc;
                    var thread = new Thread(() => updateFunc(viewModel));
                    thread.Start();
                    activeThreads.Add(thread);
                }
            }
            return viewModel;
        }

        private ViewResult GetView<X>(X viewModel) where X : T
        {
            EnsureAsyncUpdateCompletion();

            if (onCompleteActions != null)
                onCompleteActions(viewModel); 
            
            return base.View(viewModel);
        }

        private ViewResult GetView<X>(string viewName, X viewModel) where X : T
        {
            EnsureAsyncUpdateCompletion();

            if (onCompleteActions != null)
                onCompleteActions(viewModel);

            return base.View(viewName, viewModel);
        }

        private void EnsureAsyncUpdateCompletion()
        {
            activeThreads.ForEach(t => t.Join());
            activeThreads.Clear();
        }

        protected virtual new ViewResult View()
        {
            var viewModel = GetViewModel<T>();
            return GetView(viewModel);
        }

        protected virtual new ViewResult View(string viewName)
        {
            var viewModel = GetViewModel<T>();
            return GetView(viewName, viewModel);
        }

        protected virtual ViewResult View(Action<T> viewModelUpdate)
        {
            var viewModel = GetViewModel<T>();
            viewModelUpdate(viewModel);
            return GetView(viewModel);
        }

        protected virtual ViewResult View(string viewName, Action<T> viewModelUpdate)
        {
            var viewModel = GetViewModel<T>();
            viewModelUpdate(viewModel);
            return GetView(viewName, viewModel);
        }

        protected virtual ViewResult View<X>() where X : T, new()
        {
            var viewModel = GetViewModel<X>();
            return GetView(viewModel);
        }

        protected virtual ViewResult View<X>(Action<X> specializedViewModelUpdate) where X : T, new()
        {
            var viewModel = GetViewModel<X>();
            specializedViewModelUpdate(viewModel);
            return GetView(viewModel);
        }

        protected virtual ViewResult View<X>(string viewName) where X : T, new()
        {
            var viewModel = GetViewModel<X>();
            return GetView(viewName, viewModel);
        }

        protected virtual ViewResult View<X>(string viewName, Action<X> specializedViewModelUpdate) where X : T, new()
        {
            var viewModel = GetViewModel<X>();
            specializedViewModelUpdate(viewModel);
            return GetView(viewName, viewModel);
        }

        protected int Pager_GetStart(int? page, int pageSize)
        {
            return pageSize * (page.GetValueOrDefault(1) - 1) + 1;
        }

        protected string GetString(string code)
        {
            return I18n.GetString(code);
        }

        protected override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            try
            {
                var msgCode = Request["message"] as string;
                if (!msgCode.Empty()) UpdateDefaultViewModel(v => v.MessageCode = msgCode);
            }
            catch { }

            base.OnActionExecuting(filterContext);            
        }

        public EntityAddition<TElement, TElement> AddEntity<TElement>(Action<TElement> entityUpdate, params IModification[] entityFieldUpdates)
            where TElement : class, IEntity, new()
        {
            return Modification.AddEntity<TElement>(entityUpdate, entityFieldUpdates);
        }

        public EntityUpdation<TElement, TElement> UpdateEntity<TElement>(Action<TElement> entityUpdate,
            params IModification[] entityFieldUpdates) where TElement : class, new()
        {
            return Modification.UpdateEntity<TElement>(entityUpdate, entityFieldUpdates);
        }
    }
}
